package cn.dansj.common.request.configure;

import javax.servlet.ServletException;

import org.apache.catalina.Container;
import org.apache.catalina.Context;
import org.apache.catalina.core.StandardWrapper;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.boot.web.server.WebServer;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.HandlerMapping;

import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

/**
 * {@code DispatcherServletHandlerMappings} provides access to a {@link DispatcherServlet
 * DispatcherServlet's} handler mappings, triggering initialization of the dispatcher
 * servlet if necessary.
 *
 * @author Andy Wilkinson
 */
public class DispatcherServletHandlerMappings {
    private final String name;
    private final DispatcherServlet dispatcherServlet;
    private final WebApplicationContext applicationContext;

    public DispatcherServletHandlerMappings(String name, DispatcherServlet dispatcherServlet, WebApplicationContext applicationContext) {
        this.name = name;
        this.dispatcherServlet = dispatcherServlet;
        this.applicationContext = applicationContext;
    }

    List<HandlerMapping> getHandlerMappings() {
        List<HandlerMapping> handlerMappings = this.dispatcherServlet.getHandlerMappings();
        if (handlerMappings == null) {
            initializeDispatcherServletIfPossible();
            handlerMappings = this.dispatcherServlet.getHandlerMappings();
        }
        return (handlerMappings != null) ? handlerMappings : Collections.emptyList();
    }

    private void initializeDispatcherServletIfPossible() {
        if (!(this.applicationContext instanceof ServletWebServerApplicationContext)) {
            return;
        }
        ServletWebServerApplicationContext webServerApplicationContext = (ServletWebServerApplicationContext) this.applicationContext;
        WebServer webServer = webServerApplicationContext.getWebServer();
        if (webServer instanceof TomcatWebServer) {
            TomcatWebServer tomcatWebServer = (TomcatWebServer) webServer;
            new TomcatServletInitializer(tomcatWebServer).initializeServlet(this.name);
        }
    }

    public static class TomcatServletInitializer {
        private final TomcatWebServer webServer;

        public TomcatServletInitializer(TomcatWebServer webServer) {
            this.webServer = webServer;
        }

        void initializeServlet(String name) {
            findContext().ifPresent((context) -> initializeServlet(context, name));
        }

        private Optional<Context> findContext() {
            return Stream.of(this.webServer.getTomcat().getHost().findChildren()).filter(Context.class::isInstance).map(Context.class::cast).findFirst();
        }

        private void initializeServlet(Context context, String name) {
            Container child = context.findChild(name);
            if (child instanceof StandardWrapper) {
                try {
                    StandardWrapper wrapper = (StandardWrapper) child;
                    wrapper.deallocate(wrapper.allocate());
                } catch (ServletException ignore) {
                }
            }
        }
    }
}
